﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ProjectExtensions.cs" company="Roman '4ux-nbIx' Novitsky">
//   Copyright (c) 2012 Roman '4ux-nbIx' Novitsky
// </copyright>
// <summary>
//   Defines Project class extension methods.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace MonoDevAssist
{
  using System;
  using System.Collections.Generic;
  using System.IO;
  using System.Runtime.InteropServices;

  using EnvDTE;

  using Microsoft.VisualStudio.Shell.Flavor;
  using Microsoft.VisualStudio.Shell.Interop;

  using Constants = EnvDTE.Constants;
  using IServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;


  /// <summary>
  /// Defines Project class extension methods.
  /// </summary>
  internal static class ProjectExtensions
  {
    #region Public Methods

    public static ProjectItem FindItemCopy(this Project project, ProjectItem sourceItem)
    {
      return FindItemCopy(project.ProjectItems, sourceItem);
    }


    public static ProjectItem FindItem(this Project project, string fileName)
    {
      return FindItem(project.ProjectItems, fileName);
    }


    public static List<Project> GetProjectCopies(this Project sourceProject)
    {
      var projectCopies = new List<Project>();

      DTE dte = sourceProject.DTE;

      string projectDirectory = Path.GetDirectoryName(sourceProject.FileName);

      IEnumerable<Project> projects = dte.GetSolutionProjects();

      foreach (Project project in projects)
      {
        if (project == sourceProject || string.IsNullOrEmpty(project.FileName))
        {
          continue;
        }

        string directoryName = Path.GetDirectoryName(project.FileName);

        if (directoryName != projectDirectory)
        {
          continue;
        }

        projectCopies.Add(project);
      }

      return projectCopies;
    }


    public static bool IsFolder(this ProjectItem projectItem)
    {
      switch (projectItem.Kind)
      {
        case Constants.vsProjectItemKindPhysicalFolder:
        case Constants.vsProjectItemKindVirtualFolder:
          return true;
      }

      return false;
    }


    public static string GetTypeGuids(this Project proj)
    {
      var projectTypeGuids = string.Empty;
      IVsHierarchy hierarchy;

      var service = GetService(proj.DTE, typeof(IVsSolution));
      var solution = (IVsSolution)service;

      var result = solution.GetProjectOfUniqueName(proj.UniqueName, out hierarchy);

      if (result == 0)
      {
        if (hierarchy is IVsAggregatableProjectCorrected)
        {
          var aggregatableProject = (IVsAggregatableProjectCorrected)hierarchy;
          aggregatableProject.GetAggregateProjectTypeGuids(out projectTypeGuids);
        }
      }

      return projectTypeGuids;
    }


    public static Guid GetGuid(this Project proj)
    {
      IVsHierarchy hierarchy;

      var service = GetService(proj.DTE, typeof(IVsSolution));
      var solution = (IVsSolution)service;

      var result = solution.GetProjectOfUniqueName(proj.UniqueName, out hierarchy);

      if (result == 0)
      {
        Guid guid;
        solution.GetGuidOfProject(hierarchy, out guid);

        return guid;
      }

      return Guid.Empty;
    }


    public static bool IsMonoTouchProject(this Project project)
    {
      var projectTypeGuids = project.GetTypeGuids();

      return projectTypeGuids.Contains(GuidList.MonoTouch26ProjectTypeGuidString)
             || projectTypeGuids.Contains(GuidList.MonoTouch28ProjectTypeGuidString);
    }


    public static bool IsMonoAndroidLibraryProject(this Project project)
    {
      var projectTypeGuids = project.GetTypeGuids();

      return projectTypeGuids.Contains(GuidList.MonoAndroidProjectTypeGuidString);
    }

    public static string GetDefaultNamespace(this Project project)
    {
      return project.Properties.Item("RootNamespace").Value as string;
    }


    /// <summary>
    /// Determines whether the specified project is a solution folder.
    /// </summary>
    /// <param name="project">
    /// The project.
    /// </param>
    /// <returns>
    /// <c>true</c> if the specified project is a solution folder; otherwise, <c>false</c>.
    /// </returns>
    public static bool IsSolutionFolder(this Project project)
    {
      return project.Kind == Constants.vsProjectKindSolutionItems;
    }


    /// <summary>
    /// Gets the solution folder projects.
    /// </summary>
    /// <param name="project">The project.</param>
    /// <returns>
    /// The solution folder projects.
    /// </returns>
    public static IEnumerable<Project> GetSolutionFolderProjects(this Project project)
    {
      var projects = new List<Project>();

      if (!project.IsSolutionFolder())
      {
        return projects;
      }

      for (int i = 1; i <= project.ProjectItems.Count; i++)
      {
        ProjectItem item = project.ProjectItems.Item(i);

        if (item == null)
        {
          continue;
        }

        var folderProject = item.Object as Project;

        if (folderProject == null)
        {
          continue;
        }

        if (folderProject.IsSolutionFolder())
        {
          projects.AddRange(folderProject.GetSolutionFolderProjects());
          continue;
        }

        projects.Add(folderProject);
      }

      return projects;
    }

    #endregion


    #region Methods

    private static object GetService(object serviceProvider, Type type)
    {
      return GetService(serviceProvider, type.GUID);
    }

    private static object GetService(object serviceProviderObject, Guid guid)
    {
      object service = null;
      IntPtr serviceIntPtr;

      var sidGuid = guid;
      var iidGuid = sidGuid;
      var serviceProvider = (IServiceProvider)serviceProviderObject;
      var hr = serviceProvider.QueryService(ref sidGuid, ref iidGuid, out serviceIntPtr);

      if (hr != 0)
      {
        Marshal.ThrowExceptionForHR(hr);
      }
      else if (!serviceIntPtr.Equals(IntPtr.Zero))
      {
        service = Marshal.GetObjectForIUnknown(serviceIntPtr);
        Marshal.Release(serviceIntPtr);
      }

      return service;
    }

    private static ProjectItem FindItemCopy(ProjectItems projectItems, ProjectItem sourceItem)
    {
      for (int i = 1; i <= projectItems.Count; i++)
      {
        ProjectItem item = projectItems.Item(i);

        if (item.FileCount != sourceItem.FileCount)
        {
          continue;
        }

        bool allFilesAreTheSame = true;

        for (short j = 1; j <= sourceItem.FileCount; j++)
        {
          if (item.FileNames[j] == sourceItem.FileNames[j])
          {
            continue;
          }

          allFilesAreTheSame = false;
          break;
        }

        if (!allFilesAreTheSame)
        {
          if (IsFolder(item))
          {
            ProjectItem itemCopy = FindItemCopy(item.ProjectItems, sourceItem);

            if (itemCopy != null)
            {
              return itemCopy;
            }
          }

          continue;
        }

        return item;
      }

      return null;
    }


    private static ProjectItem FindItem(ProjectItems projectItems, string fileName)
    {
      for (int i = 1; i <= projectItems.Count; i++)
      {
        ProjectItem item = projectItems.Item(i);

        if (item.FileNames[1] == fileName)
        {
          return item;
        }

        if (!IsFolder(item))
        {
          continue;
        }

        item = FindItem(item.ProjectItems, fileName);

        if (item != null)
        {
          return item;
        }
      }

      return null;
    }

    #endregion
  }
}